package com.zone.infrastructure.persistent.repository;

import cn.hutool.core.util.NumberUtil;
import com.google.gson.Gson;
import com.zone.domain.award.model.valobj.UserCreditAccountStatus;
import com.zone.domain.credit.event.UserCreditAdjustEvent;
import com.zone.domain.credit.model.aggregate.CreditAdjustAggregate;
import com.zone.domain.credit.model.entity.UserCreditAccountEntity;
import com.zone.domain.credit.model.entity.UserCreditOrderEntity;
import com.zone.domain.credit.model.valobj.TradeTypeEnum;
import com.zone.domain.credit.repository.ICreditRepository;
import com.zone.domain.task.model.entity.TaskEntity;
import com.zone.domain.task.model.valobj.TaskState;
import com.zone.infrastructure.event.MessageProduct;
import com.zone.infrastructure.persistent.dao.ITaskDao;
import com.zone.infrastructure.persistent.dao.IUserCreditAccountDao;
import com.zone.infrastructure.persistent.dao.IUserCreditOrderDao;
import com.zone.infrastructure.persistent.po.Task;
import com.zone.infrastructure.persistent.po.UserCreditAccount;
import com.zone.infrastructure.persistent.po.UserCreditOrder;
import com.zone.infrastructure.persistent.redis.IRedisService;
import com.zone.types.common.Constants;
import com.zone.types.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigDecimal;
import java.util.Objects;

/**
 * @author: zongzi
 * @description: TODO
 * @date: 2025/3/11
 */
@Slf4j
@Repository
public class CreditRepository implements ICreditRepository {
    @Autowired
    private IUserCreditAccountDao userCreditAccountDao;
    @Autowired
    private IUserCreditOrderDao userCreditOrderDao;
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private IRedisService redisService;
    @Autowired
    private MessageProduct messageProduct;
    @Autowired
    private ITaskDao taskDao;

    @Override
    public void adjustAccountAmount(CreditAdjustAggregate creditAdjustAggregate) {
        // 用户积分账户
        UserCreditAccount userCreditAccount = new UserCreditAccount();
        userCreditAccount.setUserId(creditAdjustAggregate.getUserId());
        userCreditAccount.setTotalAmount(creditAdjustAggregate.getAmount());
        userCreditAccount.setAvailableAmount(creditAdjustAggregate.getAmount());
        userCreditAccount.setAccountStatus(UserCreditAccountStatus.OPEN.getCode());
        // 用户积分流水单
        UserCreditOrderEntity userCreditOrderEntity = creditAdjustAggregate.getUserCreditOrderEntity();
        UserCreditOrder userCreditOrder = new UserCreditOrder();
        userCreditOrder.setUserId(creditAdjustAggregate.getUserId());
        userCreditOrder.setOrderId(userCreditOrderEntity.getOrderId());
        userCreditOrder.setTradeName(userCreditOrderEntity.getTradeName());
        userCreditOrder.setTradeType(userCreditOrderEntity.getTradeType());
        userCreditOrder.setTradeAmount(creditAdjustAggregate.getAmount());
        userCreditOrder.setOutBusinessNo(userCreditOrderEntity.getOutBusinessNo());
        // 任务
        TaskEntity taskEntity = creditAdjustAggregate.getTaskEntity();
        Task task = new Task();
        task.setUserId(taskEntity.getUserId());
        task.setTopic(taskEntity.getTopic());
        task.setMessageId(taskEntity.getMessageId());
        task.setMessage(taskEntity.getMessage());
        task.setState(taskEntity.getState().getCode());

        String accountLockKey = Constants.RedisKey.generate(Constants.RedisKey.USER_CREDIT_ACCOUNT_LOCK, creditAdjustAggregate.getUserId());
        RLock lock = redisService.getLock(accountLockKey);
        try {
            // 加锁
            lock.lock();
            transactionTemplate.execute(status -> {
                try {
                    // 查询用户积分账户
                    UserCreditAccount existedCreditAccount = userCreditAccountDao.queryUserCreditAccount(userCreditAccount.getUserId());
                    if (TradeTypeEnum.FORWARD.getCode().equals(creditAdjustAggregate.getTradeTypeEnum().getCode())) {
                        if (Objects.isNull(existedCreditAccount)) {
                            userCreditAccountDao.insert(userCreditAccount);
                        } else {
                            // 新增积分
                            userCreditAccountDao.addAmount(userCreditAccount);
                        }
                    } else if (TradeTypeEnum.REVERSE.getCode().equals(creditAdjustAggregate.getTradeTypeEnum().getCode())) {
                        boolean less = Objects.isNull(existedCreditAccount) || NumberUtil.isLess(existedCreditAccount.getAvailableAmount(), creditAdjustAggregate.getAmount());
                        if (less) {
                            BigDecimal availableAmount = Objects.isNull(existedCreditAccount) ? BigDecimal.ZERO : existedCreditAccount.getAvailableAmount();
                            String errorMsg = String.format("积分余额不足, 剩余可用积分[%s]", availableAmount);
                            throw new BizException(errorMsg);
                        }
                        // 扣减积分
                        userCreditAccountDao.subAmount(userCreditAccount);
                    }

                    // 保存用户积分流水单
                    userCreditOrderDao.insert(userCreditOrderEntity);
                    // 保存任务
                    taskDao.insert(task);
                } catch (Exception e) {
                    status.setRollbackOnly();
                    throw e;
                }
                return 1;
            });
        } finally {
            // 解锁
            lock.unlock();
        }

        // 发送消息
        try {
            // 发送mq
            Gson gson = new Gson();
            UserCreditAdjustEvent userCreditAdjustEvent = gson.fromJson(taskEntity.getMessage(), UserCreditAdjustEvent.class);
            messageProduct.sendMessage(task.getTopic(), userCreditAdjustEvent);
            // 更新task状态
            task.setState(TaskState.COMPLETED.getCode());
            taskDao.updateStateByMessageId(task);
        } catch (Exception e) {
            log.error("发送用户积分变更消息失败", e);
            // 更新task状态
            task.setState(TaskState.FAIL.getCode());
            taskDao.updateStateByMessageId(task);
        }
    }

    @Override
    public UserCreditAccountEntity queryCreditAccount(String userId) {
        UserCreditAccount userCreditAccount = userCreditAccountDao.queryUserCreditAccount(userId);
        if (Objects.isNull(userCreditAccount)) {
            return null;
        }
        UserCreditAccountEntity userCreditAccountEntity = new UserCreditAccountEntity();
        userCreditAccountEntity.setUserId(userCreditAccount.getUserId());
        userCreditAccountEntity.setTotalAmount(userCreditAccount.getTotalAmount());
        userCreditAccountEntity.setAvailableAmount(userCreditAccount.getAvailableAmount());
        userCreditAccountEntity.setAccountStatus(userCreditAccount.getAccountStatus());
        return userCreditAccountEntity;
    }
}
